# -*- coding:utf-8 -*-
import numpy as np
import random
import os
import tensorflow.compat.v1 as tf

tf.disable_v2_behavior()

import Map

class DQN():
    def __init__(self):
        self.n_input = Map.mapsize * Map.mapsize
        self.n_output = 1
        self.current_q_step = 0
        self.avg_loss = 0
        # placeholder是在神经网络构建graph的时候在模型中的占位，此时并没有把要输入的数据传入模型，它只会分配必要的内存。
        # 建立完session后，在会话中，运行模型的时候通过feed_dict()函数向占位符喂入数据。
        self.x = tf.placeholder("float", [None, Map.mapsize, Map.mapsize], name='x')
        self.y = tf.placeholder("float", [None, self.n_output], name='y')
        self.create_Q_network()
        self.create_training_method()
        self.saver = tf.train.Saver()
        self.sess = tf.Session()
        # 它能让你在运行图的时候，插入一些计算图
        self.sess = tf.InteractiveSession()
        self.sess.run(tf.global_variables_initializer())

    def create_Q_network(self):
        # [作业内容]

    def conv_basic(self, _input, _w, _b):
        # [作业内容]

    def create_training_method(self):
        # [作业内容]

    def restore(self):
        if os.path.exists('Saver/cnnsaver.ckpt-0.index'):
            self.saver.restore(self.sess, os.path.abspath('Saver/cnnsaver.ckpt-0'))

    def computerPlay(self, IsTurnWhite):
        if IsTurnWhite:
            print('白棋走')
            # 如果该白棋走的话 用黑的棋盘，1代表黑，-1代表白
            board = np.array(Map.blackBoard)
        else:
            print('黑棋走')
            # 如果该黑棋走的话 用白的棋盘 1代表白，-1代表黑
            board = np.array(Map.whiteBoard)
        # 建立所有可下位置的数组，每下一个位置一个数组
        boards = []
        # 当前棋谱中空白的地方
        positions = []
        for i in range(Map.mapsize):
            for j in range(Map.mapsize):
                # 如果这个当前棋谱这个位置是空白的
                if board[j][i] == Map.backcode:
                    predx = np.copy(board)
                    # -1代表自己，更方便计算
                    predx[j][i] = -1
                    boards.append(predx)
                    positions.append([i, j])
        if len(positions) == 0:
            return 0, 0, 0
        # 计算所有可下的位置的价值
        nextStep = self.sess.run(self.Q_value, feed_dict={self.x: boards})
        maxx = 0
        maxy = 0
        maxValue = -1000  # 实际最大价值  用于后续学习
        # 从所有可下的地方找一个价值最大的位置下棋
        for i in range(len(positions)):
            value = nextStep[i] + random.randint(0, 10) / 1000  # 如果没有最优步子 则随机选择一步
            if value > maxValue:
                maxValue = value
                maxx = positions[i][0]
                maxy = positions[i][1]
        print(str(maxx) + ',' + str(maxy))
        print('此位置的价值为：' + str(maxValue[0]))
        return maxx, maxy, maxValue

    # 下完了一局就更新一下AI模型
    def TrainOnce(self, winner):
        # 记录棋图
        # board1 白棋 board2 黑棋
        board1 = np.array(Map.mapRecords1)
        board2 = np.array(Map.mapRecords2)
        # 记录棋步
        step1 = np.array(Map.stepRecords1)
        step2 = np.array(Map.stepRecords2)
        # 记录得分
        scoreR1 = np.array(Map.scoreRecords1)
        scoreR2 = np.array(Map.scoreRecords2)
        board1 = np.reshape(board1, [-1, Map.mapsize, Map.mapsize])
        board2 = np.reshape(board2, [-1, Map.mapsize, Map.mapsize])
        step1 = np.reshape(step1, [-1, Map.mapsize, Map.mapsize])
        step2 = np.reshape(step2, [-1, Map.mapsize, Map.mapsize])

        score1 = []
        score2 = []

        board1 = (board1 * (1 - step1)) + step1 * Map.blackcode
        board2 = (board2 * (1 - step2)) + step2 * Map.blackcode
        # 每步的价值 = 奖励（胜1 负-0.9） + 对方棋盘能达到的最大价值（max taget Q） * （-0.9）
        for i in range(len(board1)):
            if i == len(scoreR2):  # 白方已经三连  白方赢
                print('白方已经三连，白方赢')
                score1.append([1.0])  # 白方的最后一步获得1分奖励
            else:
                # 白方的价值为：黑方棋盘能达到的最大价值（max taget Q） * （-0.9）
                score1.append([scoreR2[i][0] * -0.9])
        if winner == 2:
            print('惩罚白方的最后一步，将其价值设为 -0.9')
            score1[len(score1) - 1][0] = -0.9

        # 1 白棋 2 黑棋
        for i in range(len(board2)):
            if i == len(scoreR1) - 1:  # 黑方赢
                print('黑方已经三连，黑方赢')
                score2.append([1.0])
            else:
                # 黑棋的得分为：白方棋盘能达到的最大价值（max taget Q） * （-0.9）
                score2.append([scoreR1[i + 1][0] * -0.9])
        if winner == 1:
            print('惩罚黑方的最后一步，将其价值设为 -0.9')
            # 惩罚黑方的最后一步
            score2[len(score2) - 1][0] = -0.9

        # 一次完成多个数组的拼接
        borders = np.concatenate([board1, board2], axis=0)
        scores = np.concatenate([score1, score2], axis=0)
        _, totalLoss = self.sess.run([self.optm, self.cost], feed_dict={self.x: borders,
                                                                        self.y: scores})
        self.avg_loss += totalLoss
        print('train avg loss ' + str(self.avg_loss))
        self.avg_loss = 0
        # os.path.abspath取决于os.getcwd,如果是一个绝对路径，就返回，
        # 如果不是绝对路径，根据编码执行getcwd/getcwdu.然后把path和当前工作路径连接起来
        self.saver.save(self.sess, os.path.abspath('Saver/cnnsaver.ckpt'), global_step=0)

    def PlayWidthHuman(self):
        # 读取历史存储的模型
        self.restore()
        Map.PlayWithComputer = self.computerPlay
        Map.TrainNet = self.TrainOnce
        Map.ShowWind()


if __name__ == '__main__':
    dqn = DQN()
    dqn.PlayWidthHuman()